#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>


using namespace std;

template<class T>
class BlockQueue
{
    static const int defaultcap = 8;
public:
    BlockQueue(int capacity = defaultcap)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond_productor, nullptr);
        pthread_cond_init(&_cond_consumer, nullptr);
        //_low_water = 3;
        //_high_water = 5;
    }

    // 消费者消耗数据
    T pop()
    {
        pthread_mutex_lock(&_mutex);
        while(_q.size() == 0)
        {
            // 在调用的时候,自动释放锁
            pthread_cond_wait(&_cond_consumer, &_mutex); 
        }
        T x = _q.front();
        _q.pop(); // 当前线程想消费就能消费吗?不一定,你首先得确保消费条件满足
        //if(_q.size() < _low_water)  // 通知生产者来生产
        pthread_cond_signal(&_cond_productor);
        pthread_mutex_unlock(&_mutex);
        return x;
    }

    void push(const T& x)
    {
        pthread_mutex_lock(&_mutex);
        // 判断本身也是临界资源,如果放外面,就会资源并发访问
        while(_q.size() == _capacity)
        {
            // 在调用的时候,自动释放锁
            pthread_cond_wait(&_cond_productor, &_mutex); 
        }
        // 1.队列没满 2.被唤醒
        _q.push(x);// 当前线程想生产就能生产吗?不一定,你首先得确保生产条件满足
        //if(_q.size() > _high_water) // 通知消费者来消费
        pthread_cond_signal(&_cond_consumer);
        // 当前线程push的时候，也没有其他线程pop呢?所以我们要加锁
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond_consumer);
        pthread_cond_destroy(&_cond_productor);
    }
private:
    queue<T> _q; // 共享资源,q被当作整体使用的
    // 如果q看作整体，我们就要加锁,
    // 但是共享资源可以被分成多份
    // 每个线程访问自己分出来的那部分资源，就可以做到并发访问公共资源
    // 分了多少份资源,就只能有多少个线程来访问
    // 此时如何保证只有这么多线程来访问分出来资源,使用信号量
    int _capacity; //极值

    // 实现多线程之间的同步问题
    pthread_mutex_t _mutex;
    pthread_cond_t _cond_consumer;
    pthread_cond_t _cond_productor;

    //int _low_water;
    //int _high_water;
};